package org.covalence.core;

import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.log4j.Logger;

/**
 * Maintains cache of class configuration data.  As the EnhancingAgent loads
 * the class files it will scan the fields and methods for JavaBean style
 * properties and register the field, method and type data in the Registry.
 * 
 * It allows dynamic configurable list of classes which are to be enhanced.
 * 
 * @author tslusser
 *
 */
@SuppressWarnings("unchecked")
public class Registry {

	private static Map<String, ClassConfig> registry = 
		Collections.synchronizedMap(new HashMap<String, ClassConfig>());

	private static Set classNames = new HashSet();
	
	private static Logger log = Logger.getLogger(Registry.class);
	
	static {
		try {
			InputStream in = Thread.currentThread().getContextClassLoader().
					getResourceAsStream("bind.properties");
			if (in != null) {
				Properties p = new Properties();
				p.load(in);
				for (Object each : p.keySet())
					register((String) each);
				in.close();
				log.trace("Registered classes:" + classNames);
			} else {
				log.info("'bind.properties' not loadable.  Enhanceable classes must be explicitly added to Registry.");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Checks whether a class is registered for enhancement
	 * @param clazz
	 * @return
	 */
	public static boolean isObserved(Class<?> clazz) {
		return registry.containsKey(clazz.getName().replace('.', '/'));
	}
	
	/**
	 * Checks whether a class is registered for enhancement
	 * @param className
	 * @return
	 */
	public static boolean isObserved(String className) {
		boolean result = registry.containsKey(
				Strings.descriptorToClassname(className));
		return result;
	}

	/**
	 * Checks if the class has been configured to be enhanced 
	 * @param className e.g. "org/covalence/POJO" or "org.covalence.POJO"
	 * @return true if class is enhanceable otherwise false
	 */
	public static boolean shouldEnhance(String className) {
		return classNames.contains(className);
	}
	
	/**
	 * Programmatic way of adding enhanceable classes to Registry 
	 * @param className
	 */
	public static void register(String className) {
		if (className.indexOf('.') > -1 ) {
			className = className.replace('.', '/');
		}
		classNames.add(className);
	}
	
	/**
	 * Registers a ClassConfig specifying the class and properties which are
	 * to be modified to make observable
	 * @param cc
	 */
	public static void register(ClassConfig cc) {
		registry.put(cc.getClassName().replace('.', '/'), cc);
	}
	
	/**
	 * Finds the ClassConfig for the given class
	 * @param className the className e.g. "org/covalence/POJO"
	 * @return null if no ClassConfig registered
	 */
	public static ClassConfig getClassConfig(String className) {
		return registry.get(className);
	}
}
